home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C
/
Snippets
/
Stuart's Tech Notes
/
AppleTalk Zone List.c
< prev
next >
Wrap
Text File
|
1994-12-12
|
8KB
|
269 lines
/* (C) 1993 Stuart Cheshire <cheshire@cs.stanford.edu>
This sample code can be used to obtain a list of AppleTalk Zones on the
network. It uses an efficient sorting algorithm to sort a large number
zone names into alphabetical order in an acceptable length of time.
I'm making this public because a shocking number of programs, like MacTCP,
Shiva Net Manager, Spectre Supreme etc. are unable to do this. The Stanford
network has over 200 zones, and on a Mac SE it takes *over a minute* just
to open the MacTCP Control Panel!
MAX_ZONES is set to 1000 by default, which should be enough for most networks.
You should create a List Manager list, and then call getzonelist to fill it.
eg.
static Rect initial_list = { 0,0,0,1 };
static Point list_cell_size = { 0,0 };
GetDItem(modaldialog, zonelist_item, &di_type, &di_handle, &di_box);
// Read useritem bounds from DITL
di_box.right -= 15; // Make room for the vertical scroll bar
zonelist = LNew(&di_box, &initial_list, list_cell_size, 0, modaldialog,
TRUE, FALSE, FALSE, TRUE); // Create the list
LDoDraw(FALSE, zonelist); // Turn off list drawing
num_appletalk_zones = getzonelist(zonelist, mydomain.c);
// Get the list of zones
pt.h = pt.v = 0;
LSearch(&myzone.c[1], myzone.c[0], NULL, &pt, zonelist);
LSetSelect(TRUE, pt, zonelist); // select home zone as default
LAutoScroll(zonelist); // scroll it into view
LDoDraw(TRUE, zonelist); // and turn on list drawing again.
Tab stops are four spaces
/*****************************************************************************/
#include <AppleTalk.h>
#include <Packages.h>
#include "StuTypes.h"
#include "StuAppleTalk.h"
/*****************************************************************************/
#define atpMaxData 578 // size of buffer for zone names
#define kZIPSocket 6 // the Zone Information Protocol socket
#define kATPTimeOutVal 3 // re-try ATP SendRequest every 3 seconds
#define kATPRetryCount 5 // for five times
#define kGZLCall 0x08000000 // GetZoneList indicator
#define kGMZCall 0x07000000 // GetMyZone indicator
#define kZoneCount 0x0000FFFF // mask to count zones in buffer
#define kNoMoreZones 0xFF000000 // mask to see if more zones to come
#define xCall 246
#define zipGetLocalZones 5
#define zipGetZoneList 6
#define zipGetMyZone 7
typedef struct
{
QElemPtr qLink;
short qType;
short ioTrap;
Ptr ioCmdAddr;
ProcPtr ioCompletion;
OSErr ioResult;
StringPtr ioNamePtr;
short ioVRefNum;
short ioRefNum;
short csCode;
short xppSubCode;
u_char xppTimeOut;
u_char xppRetry;
short filler;
Ptr zipBuffPtr;
short zipNumZones;
short zipLastFlag;
short zipInfoField[35];
} xCallParam;
/*****************************************************************************\
These global variables hold the zone names, the heap of pointers used to
sort them, and the counter which says how many zones there are on the heap.
The code could be made more elegant (and re-entrant) if the routines were
wrapped up into a "ZoneList" C++ object, and the globals could then be made
private class member variables.
*/
#define MAX_ZONES 1000
typedef struct { u_char c[34]; } ZONE_NAME;
local ZONE_NAME *zone_array, **zone_heap;
local short num_zones;
/*****************************************************************************\
These two routines are used to sort the zone list into alphabetical order
*/
local void ZL_addtoheap(int place, ZONE_NAME *new)
{
int parent = (place-1)>>1;
zone_heap[place]=new;
while (place>0 && IUCompString(zone_heap[parent]->c, zone_heap[place]->c)>0)
{
ZONE_NAME *temp = zone_heap[place ];
zone_heap[place ] = zone_heap[parent];
zone_heap[parent] = temp;
place=parent;
parent=(place-1)>>1;
}
}
local ZONE_NAME *ZL_removefromheap(int last)// last is index of last item on heap
{
ZONE_NAME *top = zone_heap[0]; // extract top element
int gap = 0; // top is now empty
int left = 1;
int right = 2;
while(left<=last) // move gap to bottom
{
if(right>last || IUCompString(zone_heap[right]->c, zone_heap[left]->c)>0)
{ zone_heap[gap]=zone_heap[left ]; gap=left; }
else{ zone_heap[gap]=zone_heap[right]; gap=right; }
left = (gap<<1)+1;
right = left + 1;
}
if(last != gap) ZL_addtoheap(gap, zone_heap[last]); // fill gap
return(top); // and heap is now one element smaller
}
/*****************************************************************************\
This routine (the old method) is used if AppleTalk Phase Two is not available
*/
local void BuildZoneListPhase1(u_char *myzone)
{
ATPParamBlock pb;
BDSElement dBDS;
u_char datapacket[atpMaxData];
short i;
dBDS.buffSize = atpMaxData;
dBDS.buffPtr = (Ptr)datapacket;
pb.ATPatpFlags = 0;
pb.ATPreqLength = 0;
pb.ATPreqPointer = NULL;
pb.ATPbdsPointer = (Ptr) &dBDS;
pb.ATPnumOfBuffs = 1;
pb.ATPtimeOutVal = kATPTimeOutVal;
pb.ATPretryCount = kATPRetryCount;
pb.ATPaddrBlock.aNet = ABusVars->sysNetNum;
pb.ATPaddrBlock.aNode = GetBridgeAddress();
pb.ATPaddrBlock.aSocket = kZIPSocket;
if (!pb.ATPaddrBlock.aNode) return; // no bridge -- no zones
do {
u_char *ptr = datapacket;
pb.ATPuserData = kGZLCall + num_zones + 1;
PSendRequest(&pb, false);
i = dBDS.userBytes & kZoneCount;
while(--i >= 0)
{
int count = 1 + *ptr;
u_char *dest = zone_array[num_zones].c;
while (count-- > 0) *dest++ = *ptr++;
ZL_addtoheap(num_zones, &zone_array[num_zones]);
num_zones++;
}
} until(dBDS.userBytes & kNoMoreZones);
pb.ATPuserData = kGMZCall;
datapacket[0] = 0;
if (PSendRequest(&pb, false) == noErr && datapacket[0] && datapacket[0]<32)
for (i=0; i<=datapacket[0]; i++) myzone[i] = datapacket[i];
}
/*****************************************************************************\
This routine (the new method) should be used in almost all cases these days
*/
local void BuildZoneListPhase2(u_char *myzone)
{
short XPPRefNum;
xCallParam xpb;
u_char datapacket[atpMaxData];
if (OpenDriver("\p.XPP", &XPPRefNum)) return;
xpb.zipInfoField[0] = 0;
xpb.zipInfoField[1] = 0;
xpb.zipLastFlag = FALSE;
xpb.ioRefNum = XPPRefNum;
xpb.csCode = xCall;
xpb.xppSubCode = zipGetZoneList;
xpb.xppTimeOut = kATPTimeOutVal;
xpb.xppRetry = kATPRetryCount;
xpb.zipBuffPtr = (Ptr)datapacket;
while (!xpb.zipLastFlag && PBControl((ParmBlkPtr)&xpb, FALSE) == noErr)
{
u_char *ptr = datapacket;
short i = xpb.zipNumZones;
while(--i >= 0)
{
int namelength = 1 + *ptr;
u_char *dest = zone_array[num_zones].c;
while (namelength-- > 0) *dest++ = *ptr++;
ZL_addtoheap(num_zones, &zone_array[num_zones]);
num_zones++;
}
}
xpb.zipInfoField[0] = 0;
xpb.zipInfoField[1] = 0;
xpb.xppSubCode = zipGetMyZone;
xpb.zipBuffPtr = (Ptr)myzone;
PBControl((ParmBlkPtr)&xpb, FALSE);
// Mustn't close XPP driver (see IM V 532)
}
/*****************************************************************************\
This routine fills the given ListHandle with the list of zone names,
in alphabetical order, and also puts the name of the 'home' zone for
this Machintosh in the string pointed to by myzone. The return value
is the number of zone names put into the ListHandle.
*/
short getzonelist(ListHandle zonelist, u_char *myzone)
{
myzone[0] = num_zones = 0;
zone_array = (ZONE_NAME *) NewPtr(MAX_ZONES * sizeof(ZONE_NAME ));
zone_heap = (ZONE_NAME **) NewPtr(MAX_ZONES * sizeof(ZONE_NAME *));
if (zone_array && zone_heap)
{
short i;
SysEnvRec sysenvirons;
SysEnvirons(1, &sysenvirons);
if (sysenvirons.atDrvrVersNum < 53) BuildZoneListPhase1(myzone);
else BuildZoneListPhase2(myzone);
i = num_zones;
while (--i>=0)
{
Point pt = { 0, 0 };
ZONE_NAME *name = ZL_removefromheap(i);
pt.v = (*zonelist)->dataBounds.bottom;
LAddRow(1, pt.v, zonelist);
LSetCell(&name->c[1], name->c[0], pt, zonelist);
}
}
if (zone_array) DisposPtr((Ptr)zone_array);
if (zone_heap ) DisposPtr((Ptr)zone_heap);
return(num_zones);
}